#!/usr/bin/env python
import os
import sys
import platform
import json

URLS = ["http://storage.mds.yandex.net/get-devtools-opensource/479623/a4d31a25903254935156e2c3a2c740fb"]
MD5 = "a4d31a25903254935156e2c3a2c740fb"

RETRIES = 5
HASH_PREFIX = 10

HOME_DIR = os.path.expanduser('~')


def create_dirs(path):
    try:
        os.makedirs(path)
    except OSError as e:
        import errno

        if e.errno != errno.EEXIST:
            raise

    return path


def misc_root():
    return create_dirs(os.getenv('YA_CACHE_DIR') or os.path.join(HOME_DIR, '.ya'))


def tool_root():
    return create_dirs(os.getenv('YA_CACHE_DIR_TOOLS') or os.path.join(misc_root(), 'tools'))


TOOLS_DIR = tool_root()


def uniq(size=6):
    import string
    import random

    return ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(size))


def _fetch(url, into):
    import hashlib

    try:
        import urllib2
        conn = urllib2.urlopen(url, timeout=10)
    except ImportError:
        import urllib.request
        conn = urllib.request.urlopen(url, timeout=10)

    md5 = hashlib.md5()

    sys.stderr.write('Downloading %s [' % (url,))
    try:
        with open(into, "wb") as f:
            while True:
                block = conn.read(1024 * 1024)
                sys.stderr.write('.')
                if block:
                    md5.update(block)
                    f.write(block)
                else:
                    break
        return md5.hexdigest()

    finally:
        sys.stderr.write('] ')


def _atomic_fetch(url, into, md5):
    tmp_dest = into + '.' + uniq()
    try:
        real_md5 = _fetch(url, tmp_dest)
        if real_md5 != md5:
            raise Exception('MD5 mismatched: %s differs from %s' % (real_md5, md5))
        os.rename(tmp_dest, into)
        sys.stderr.write('OK\n')
    except Exception as e:
        sys.stderr.write('ERROR: ' + str(e) + '\n')
        raise
    finally:
        try:
            os.remove(tmp_dest)
        except OSError:
            pass


def _extract(path, into):
    import tarfile

    tar = tarfile.open(path)
    tar.extractall(path=into)
    tar.close()


def _get(urls, md5):
    dest_path = os.path.join(TOOLS_DIR, md5[:HASH_PREFIX])

    if not os.path.exists(dest_path):
        for iter in range(RETRIES):
            try:
                _atomic_fetch(urls[iter % len(urls)], dest_path, md5)
                break
            except Exception:
                if iter + 1 == RETRIES:
                    raise
                else:
                    import time
                    time.sleep(iter)

    return dest_path


def _get_dir(urls, md5):
    packed_path = _get(urls, md5)
    dest_dir = os.path.join(TOOLS_DIR, md5[:HASH_PREFIX] + '_d')

    if os.path.exists(dest_dir):
        return dest_dir

    tmp_dir = dest_dir + '.' + uniq()
    try:
        _extract(packed_path, tmp_dir)

        try:
            os.rename(tmp_dir, dest_dir)
        except OSError as e:
            import errno
            if e.errno != errno.ENOTEMPTY:
                raise

        return dest_dir
    finally:
        import shutil
        shutil.rmtree(tmp_dir, ignore_errors=True)


def _mine_arc_root():
    src_root = os.path.dirname(os.path.realpath(__file__))

    if os.path.exists(os.path.join(src_root, '.arcadia.root')):
        return src_root

    return None


def main():
    if not os.path.exists(TOOLS_DIR):
        os.makedirs(TOOLS_DIR)

    with open(_get(URLS, MD5), 'r') as fp:
        meta = json.load(fp)['data']
    my_platform = platform.system().lower()

    # match by max prefix length
    best_key = max(meta.keys(), key=lambda x: len(os.path.commonprefix([my_platform, x])))
    value = meta[best_key]

    ya_name = {'win32': 'ya-bin.exe'}.get(my_platform, 'ya-bin')  # XXX
    ya_dir = _get_dir(value['urls'], value['md5'])
    ya_path = os.path.join(ya_dir, ya_name)

    env = os.environ.copy()
    src_root = _mine_arc_root()
    if src_root is not None:
        env['YA_SOURCE_ROOT'] = src_root

    if os.name == 'nt':
        import subprocess

        p = subprocess.Popen([ya_path] + sys.argv[1:], env=env)
        p.wait()
        sys.exit(p.returncode)
    else:
        os.execve(ya_path, [ya_path] + sys.argv[1:], env)


if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        sys.stderr.write('ERROR: ' + str(e) + '\n')
        sys.exit(1)
